﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Web.Http;
using System.Web.Mvc;
using XRM.Security.DataAccess.Enums;
using XRM.Security.DataAccess.Helpers;
using XRM.Security.DataAccess.Models;
using XRM.Security.WebUI.Enums;
using XRM.Security.WebUI.Helpers;
using XRM.Security.WebUI.Models.CertRegister;
using XRM.Security.WebUI.Models.IdentityCenter;
using XRM.Security.WebUI.Models.SystemUser;
using XRM.Sag.Utils.Helpers;

namespace XRM.Security.WebUI.Controllers
{
    [System.Web.Mvc.Authorize]
    public class CertRegisterController : BaseController
    {
        private const string CertDivId = "divCert_";

        #region Index

        [System.Web.Mvc.Authorize(Roles = "Administrator, UsersReestrSertificateEditor")]
        public ActionResult Index(string text, int? expireDay, bool? showAll)
        { 
            var model = new CertRegisterListModel {
                    TextFilter = text,
                    ShowAllFilter = showAll.HasValue && showAll.Value,
                    ExpireDayFilter = expireDay
            };
            return Index(model);
        }

        [System.Web.Mvc.HttpPost]
        [System.Web.Mvc.Authorize(Roles = "Administrator, UsersReestrSertificateEditor")]
        public ActionResult Index(CertRegisterListModel model)
        {
            if (!ModelState.IsValid)
            {
                ShowAlert(AlertType.Error, GetModelErrors());
                return View("Index", model);
            }

            var certRegisters = CertRegisterRepository.GetAll();
            if ( !string.IsNullOrEmpty( model.TextFilter ) )
                certRegisters = certRegisters.Where( x => x.SerialNumber.Contains( model.TextFilter ) ||
                    (x.UserRegister.LastName + " " + x.UserRegister.FirstName + " " + x.UserRegister.MotherName).Replace("ё", "е").Contains(model.TextFilter.Replace("ё", "е")));

            if ( !model.ShowAllFilter )
                certRegisters = certRegisters.ValidCertificates();
            if ( model.ExpireDayFilter.HasValue )
                certRegisters = certRegisters.ExpiredCertificates( model.ExpireDayFilter.Value );

            model.CertList = certRegisters.ToSearchItems();
            return View("Index", model);
        }

        #endregion

        #region Edit

        [System.Web.Mvc.HttpGet]
        [System.Web.Mvc.Authorize(Roles = "Administrator, UsersReestrSertificateEditor")]
        public ActionResult Edit(int id)
        {
            var cert = CertRegisterRepository.Get( id );
            if (cert == null)
                throw new HttpResponseException(HttpStatusCode.NotFound);

            var model = new CertRegisterModel(cert);
            InitCertRegisterModel( model );

            if (Request.UrlReferrer != null && Request.UrlReferrer.AbsolutePath.Contains("UserRegister"))
            {
                model.IsFromUser = true;
                model.ReturnUrl = String.Format("{0}#{1}", Request.UrlReferrer.AbsolutePath, CertDivId);
            }
            else
            {
                model.ReturnUrl = Url.Action( "Index" );
            }
            return View(model);
        }

        [System.Web.Mvc.HttpPost]
        [System.Web.Mvc.Authorize(Roles = "Administrator, UsersReestrSertificateEditor")]
        public ActionResult Edit(CertRegisterModel model)
        {
            InitCertRegisterModel(model);
            if (!ModelState.IsValid)
            {
                return View(model);
            }

            var user = UserRegisterRepository.Get(model.UserRegisterId.Value);
            if (!model.ValidateUser(user, ModelState))
            {
                return View( model );
            }
            
            CertRegister certRegister;
            CertRegisterModel oldCert = null;
            if (model.IsEdit)
            {
                certRegister = CertRegisterRepository.Get(model.Id);
                oldCert = new CertRegisterModel(certRegister);
                if (certRegister == null)
                    throw new HttpResponseException( HttpStatusCode.BadRequest );
                certRegister.CertRegisterInfoSystems.Clear();
            }
            else if ( model.DataId != null )
            {   
                var uploadedCert = Session[model.DataId] as UploadCertTempData;
                if ( uploadedCert == null )
                {
                    ShowAlert( AlertType.Error, "Сертификат уже был загружен." );
                    return RedirectToAction( "Index" );
                }
                var userCert = uploadedCert.UserCertificate;
                if (CertRegisterRepository.GetAll().Exists(userCert.Thumbprint))
                {
                    ShowAlert(AlertType.Error, String.Format("Сертификат с отпечатком '{0}' уже был загружен.", userCert.Thumbprint));
                    return View( model );
                }
                certRegister = new CertRegister();
                var issuerName = userCert.GetIssuerOrganizationName();
                var identityCenter = CertRegisterRepository.GetIdentityCenter( issuerName );
                certRegister.IdentityCenter = identityCenter ?? new IdentityCenter { IdentityCenterName = issuerName };
                certRegister.IdentityCenter.IsDeleted = false;
                certRegister.SetProperties( userCert, uploadedCert.FileName );
                if (uploadedCert.RootCertificate != null && !IdentityCenterCertRepository.GetAll().Exists(uploadedCert.RootCertificate.Thumbprint))
                {
                    IdentityCenterModel.AddCertificate(certRegister.IdentityCenter, uploadedCert.RootCertificate, uploadedCert.FileName);
                }
            }
            else
            {
                throw new HttpResponseException(HttpStatusCode.BadRequest);
            }
            
            model.ApplyChanges(certRegister);
            
            if (model.IsEdit)
            {
                CertRegisterRepository.Update( certRegister );
                ShowAlert( AlertType.Success, "Информация о сертификате успешно обновлена." );
            }
            else
            {
                CertRegisterRepository.Add( certRegister );
                WriteToRequestFromCertRegister(certRegister);

                Session.Remove(model.DataId);
                ShowAlert( AlertType.Success, "Сертификат успешно загружен." );
            }
            AddCertHistory(oldCert, model, (int?)null);

            if ( model.ReturnUrl != null )
                return Redirect( model.ReturnUrl );
            return RedirectToAction("Index");
        }

        [System.Web.Mvc.HttpPost]
        [System.Web.Mvc.Authorize( Roles = "Administrator, UsersReestrSertificateEditor" )]
        public ActionResult Add( string dataId, int? userId )
        {
            var certData = Session[dataId] as UploadCertTempData;
            if (certData == null)
                throw new HttpResponseException(HttpStatusCode.BadRequest);

            UserRegister userRegister = null;
            if (userId.HasValue)
            {
                userRegister = UserRegisterRepository.Get( userId.Value );
                if (userRegister == null || userRegister.Status != (int)UserStatus.Work)
                    throw new HttpResponseException(HttpStatusCode.BadRequest);
            }
            
            var userCert = certData.UserCertificate;
            var model = new CertRegisterModel
                {
                    FromDate = userCert.NotBefore,
                    ToDate = userCert.NotAfter,
                    SerialNumber = userCert.SerialNumber,
                    DataId = dataId,
                    IdentityCenterName = userCert.GetIssuerOrganizationName(),
                    CertificateUserName = userCert.GetNameInfo(X509NameType.SimpleName, false)
                };
            if (userRegister != null)
            {
                model.UserName = userRegister.FullName();
                model.UserRegisterId = userId;
                model.IsFromUser = true;
                model.ReturnUrl = UserRegisterReturnUrl( userId.Value );
            }
            else
            {
                var userName = userCert.GetUserName();
                var users = UserRegisterRepository.GetUsersByFio( userName ).Select( s => s.UserRegisterId ).ToArray();
                if ( users.Length == 1 )
                {
                    model.UserName = userName;
                    model.UserRegisterId = users[0];
                }
                else
                {
                    model.CertificateUserName = userName;
                }
                model.ReturnUrl = Url.Action( "Index" );
            }
            
            InitCertRegisterModel(model);
            return View("Edit", model);
        }

        private void InitCertRegisterModel(CertRegisterModel model)
        {
            model.UserPCList = new List<SelectListItem>{ SelectedListHelper.EmptyPCItem };
            if (model.UserRegisterId.HasValue)
            {
                model.UserInfoSystems = UserInfoSystemRepository.GetByUserId(model.UserRegisterId.Value).ToSelectedList();
                model.UserPCList.AddRange(UserPCRepository.GetPCByUserId(model.UserRegisterId.Value).ToSelectedList());
            }
        }

        #endregion

        [System.Web.Mvc.HttpGet]
        [System.Web.Mvc.Authorize(Roles = "Administrator, UsersReestrSertificateEditor")]
        public ActionResult Download(int id)
        {
            var cert = CertRegisterRepository.Get( id );
            if ( cert == null )
                throw new HttpResponseException( HttpStatusCode.BadRequest );

            return File( cert.FileData, "application/octet-stream", String.Format( "{0}.cer", cert.FileName ) );
        }

        [System.Web.Mvc.HttpPost]
        [System.Web.Mvc.Authorize(Roles = "Administrator, UsersReestrSertificateEditor, UsersReestrSkziEditor, UsersReestrSoftwareEditor")]
        public ActionResult Upload( int? identityCenterId )
        {
            if ( Request.Files.Count == 0 || Request.Files[0].ContentLength == 0 )
            {
                return JsonError( "Файл не выбран" );
            }
            var fileName = Request.Files[0].FileName;
            var fileExt = Path.GetExtension( fileName ).ToLower();
            if (fileExt != ".cer" && fileExt != ".p7b")
            {
                return JsonError( "Недопустимый формат файла" );
            }
            var fileStream = Request.Files[0].InputStream;
            X509Certificate2[] certificates;
            try
            {
                certificates = CertificateHelper.Parse( fileStream.ToByteArray() );
            }
            catch
            {
                return JsonError( "Загружаемый файл не является файлом сертификата" );
            }

            var uploadCertData = new UploadCertTempData
                {
                    FileName = Path.GetFileNameWithoutExtension( fileName ),
                    RootCertificate = CertificateHelper.GetRootCertificate( certificates )
                };
            X509Certificate2 cert;
            if (identityCenterId.HasValue)
            {
                if (uploadCertData.RootCertificate == null)
                    return JsonError( "Загружаемый сертификат не является корневым сертификатом УЦ" );

                cert = uploadCertData.RootCertificate;
                var issuerName = cert.GetIssuerOrganizationName();
                if (!IdentityCenterRepository.GetAll().Any(s => s.IdentityCenterId == identityCenterId && s.IdentityCenterName == issuerName))
                    return JsonError( "Загружаемый сертификат является корневым сертификатом другого УЦ" );

                if ( IdentityCenterCertRepository.GetAll( s => s.Thumbprint == cert.Thumbprint ).Any() )
                    return JsonError( String.Format( "Сертификат с отпечатком '{0}' уже был загружен", cert.Thumbprint ) );
            }
            else
            {
                cert = CertificateHelper.GetUserCertificate( certificates );
                if ( cert == null )
                    return JsonError( "Загружаемый сертификат является корневым сертификатом" );

                if ( CertRegisterRepository.GetAll( s => s.Thumbprint == cert.Thumbprint ).Any() )
                    return JsonError( String.Format( "Сертификат с отпечатком '{0}' уже был загружен", cert.Thumbprint ) );

                uploadCertData.UserCertificate = cert;
            }
            
            if (cert.NotAfter < DateTime.Today)
                return JsonError( String.Format("Срок действия сертификата истек {0:dd.MM.yyyy}", cert.NotAfter) );

            var dataId = Guid.NewGuid().ToString();
            Session[dataId] = uploadCertData;
            return Json( new { isSuccess = true, dataId = dataId } );
        }

        [System.Web.Mvc.Authorize( Roles = "Administrator, UsersReestrSertificateEditor" )]
        public ActionResult History(int page)
        {
            return History(UserHistoryGroup.Cert, page);
        }

        #region UsersDialog

        [System.Web.Mvc.HttpGet]
        [System.Web.Mvc.Authorize( Roles = "Administrator, UsersReestrSertificateEditor" )]
        public ActionResult ADUsersDialog()
        {
            ADUserDialogModel model = new ADUserDialogModel();
            model.GetDepartmentAll( DepartmentRepository.GetAll(x => x.IsDeleted == false).OrderBy( x => x.Name ).ToList() );
            model.GetDepartmentAddressAll( DepartmentAddressRepository.GetAll( x => x.DepartmentId == model.DepartmentId ).OrderBy( x => x.Address ).ToList() );

            if ( TempData["SearchUserText"] != null )
                if ( !string.IsNullOrEmpty( TempData["SearchUserText"].ToString() ) )
                    model.SearchString = TempData["SearchUserText"].ToString();

            if ( !string.IsNullOrEmpty( model.SearchString ) )
            {
                model.ADUserList = ActiveDirectoryService.SearchByFullName( model.SearchString );
            }
            return View( model );
        }

        #endregion

        private string UserRegisterReturnUrl( int userRegisterId )
        {
            return String.Format("{0}#{1}", Url.Action( "Edit", "UserRegister", new { id = userRegisterId } ), CertDivId); 
        }
    }
}
